﻿// From http://www.switchonthecode.com/tutorials/wpf-tutorial-creating-a-custom-panel-control

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace ScrumTable.UI.View.CustomControl
{
    /// <summary>
    /// WrapPane with move animation if an element changes his position
    /// </summary>
    public class WrapPanelAnimated : WrapPanel
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------
        private readonly TimeSpan _animationLength = TimeSpan.FromMilliseconds(400);

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------
        private Dictionary<int, Size> OrginalDesizredSize { get; set; }

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------
        public WrapPanelAnimated()
        {
            OrginalDesizredSize = new Dictionary<int, Size>();
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Arranges the content of a <see cref="T:System.Windows.Controls.WrapPanel"/> element.
        /// </summary>
        /// <returns>
        /// The <see cref="T:System.Windows.Size"/> that represents the arranged size of this <see cref="T:System.Windows.Controls.WrapPanel"/> element and its children.
        /// </returns>
        /// <param name="finalSize">The <see cref="T:System.Windows.Size"/> that this element should use to arrange its child elements.
        ///                 </param>
        protected override Size ArrangeOverride(Size finalSize)
        {
            return Arrange(finalSize, true);
        }

        /// <summary>
        /// Measures the child elements of a <see cref="T:System.Windows.Controls.WrapPanel"/> in anticipation of arranging them during the <see cref="M:System.Windows.Controls.WrapPanel.ArrangeOverride(System.Windows.Size)"/> pass.
        /// </summary>
        /// <returns>
        /// The <see cref="T:System.Windows.Size"/> that represents the desired size of the element.
        /// </returns>
        /// <param name="constraint">An upper limit <see cref="T:System.Windows.Size"/> that should not be exceeded.
        ///                 </param>
        protected override Size MeasureOverride(Size constraint)
        {
            if (double.IsInfinity(constraint.Width))
            {
                return new Size(0,0);
            }
            var size =  Arrange(constraint, false);
            if (double.IsInfinity(size.Width) || double.IsInfinity(size.Height))
            {
                return new Size(0, 0);
            }
            return size;
        }

        
        /// <summary>
        /// This methods arranges the items on the panal if the arrange paramter is true
        /// If arrange is false this method calculats the desired size of the panel
        /// </summary>
        /// <param name="finalSize">The size for the panel</param>
        /// <param name="arrange">true = arrange the items; false = returns the desired size</param>
        /// <returns>finalSize or DesiredSize</returns>
        private Size Arrange(Size finalSize, bool arrange)
        {
            if (Children == null || Children.Count == 0)
            {
                return finalSize;
            }

            TranslateTransform trans=null;
            double curX = 0, curY = 0, curLineHeight = 0;

            UIElement lastChild = null;
            List<UIElement> rowUiElementList = new List<UIElement>();
            for (int index = 0; index < Children.Count; index++)
            {

                UIElement child = Children[index];
                trans = GetTransformIfArrange(child, arrange, trans);
                MeasureChildIfNotArrange(finalSize, child, arrange);

                Size desiredSize = child.DesiredSize;
                
                if (curX + desiredSize.Width > finalSize.Width)
                {
                    curLineHeight = 20;
                    //Wrap to next line
                    curY += curLineHeight;
                    curX = 0;
                    curLineHeight = 0;
                }

                if (arrange)
                {
                    //arrange & start the animation
                    child.Arrange(new Rect(0, 0, desiredSize.Width, desiredSize.Height));
                    trans.BeginAnimation(TranslateTransform.XProperty, new DoubleAnimation(curX, _animationLength),
                                         HandoffBehavior.SnapshotAndReplace);
                    trans.BeginAnimation(TranslateTransform.YProperty, new DoubleAnimation(curY, _animationLength),
                                         HandoffBehavior.SnapshotAndReplace);
                }

                curX += desiredSize.Width;
                if (desiredSize.Height > curLineHeight)
                {
                    curLineHeight = desiredSize.Height;
                }
                //store the last child for later use
                lastChild = child;
                rowUiElementList.Add(child);
            }

            return arrange ? finalSize : new Size(finalSize.Width, curY + lastChild.DesiredSize.Height);
        }

        private void MeasureChildIfNotArrange(Size finalSize, UIElement child, bool arrange)
        {
            if (!arrange)
            {
                child.Measure(finalSize);
            }
        }

        private TranslateTransform GetTransformIfArrange(UIElement child, bool arrange, TranslateTransform trans)
        {
            if (arrange)
            {
                trans = child.RenderTransform as TranslateTransform;
                if (trans == null)
                {
                    child.RenderTransformOrigin = new Point(0, 0);
                    trans = new TranslateTransform();
                    child.RenderTransform = trans;
                }
            }
            return trans;
        }

        #endregion
    }
}
